﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using Microsoft.DeepZoomTools;

namespace PivotCollectionTools.BasicCollection
{
    
    /// <summary>
    /// this is what will be used to determine how the output Deep Zoom Files will be named
    /// (not implimented yet!!!!)
    /// </summary>
    public enum FileNamingConvention
    {
        /// <summary>
        /// Each file once converted will have a guid name.
        /// </summary>
        GuidBased,
        /// <summary>
        /// Each file once converted will be numerically named.
        /// </summary>
        IntBased,
        /// <summary>
        /// Each file once converted will have the same name as the orignal file 
        /// that was supplied as the file to use for the image for the item.
        /// Note that if you have to images with the same name it will simply 
        /// overwrite the old one with no warning
        /// </summary>
        KeepFileName
    }

    /// <summary>
    /// used to determin how many values a facet may have
    /// </summary>
    public enum FacetOccurrenceLimits
    {
        /// <summary>
        /// used for facets that may only have one value
        /// </summary>
        Single,
        /// <summary>
        /// used for facets that may have many values (array)
        /// </summary>
        Many
    }

    /// <summary>
    /// Each facet type will have a given base data type these are the choices
    /// </summary>
    public enum FacetTypes
    {
        /// <summary>
        /// simple DateTime format
        /// </summary>
        DateTime,
        /// <summary>
        /// this facet links to another item (Not supported yet)
        /// </summary>
        Item,
        /// <summary>
        /// facet to use for a hyperlink (Not supported yet)
        /// </summary>
        Link,
        /// <summary>
        /// long strings are useful for large chunks of data.
        /// </summary>
        LongString,
        /// <summary>
        /// 0-9 with decimals and signs you know a C# decimal
        /// </summary>
        Number,
        /// <summary>
        /// short text 
        /// </summary>
        String
    }

    /// <summary>
    /// This class will make it very easy to get started with building a pivot collection
    /// all you have to use is is make sure that you have properly applied all the needed attributes to the
    /// properties in T
    /// </summary>
    /// <typeparam name="T">Class to create a collection for</typeparam>
    public sealed class PivotCollection<T> where T : class
    {
        /// <summary>
        /// The Action Changed Event fires as the collection is creating output files
        /// to let you know what is going on.
        /// set SupressActionChanged = true if you wish to see what it is doing step by step
        /// the sender is a string that describes what it is doing.
        /// (defaults to being supressed)
        /// </summary>
        public event EventHandler ActionChanged;
        #region Basic Collection Control

        private string _outputDir;
        private bool _allImagesUnique;

        private StreamWriter _logFile;
        
        private static bool _cleanOutput = true;

        /// <summary>
        /// facets that we will try to pull from each instance of T that is added to this collection
        /// </summary>
        private static Facet[] _collectionFacets;

        /// <summary>
        /// Property that is used to pull out the description for each instance of T passed into the collection
        /// </summary>
        private static PropertyInfo _descProperty;

        /// <summary>
        /// Property that is used to pull the Link out of each instance of T passed into the collection
        /// </summary>
        private static PropertyInfo _herfProperty;

        /// <summary>
        /// Property that is used to pull out the image for each instance of T passed into the collection
        /// </summary>
        private static PropertyInfo _imageProperty;

        /// <summary>
        /// Property that is used to pull out the name for each instance of T passed into the collection
        /// </summary>
        private static PropertyInfo _nameProperty;

        /// <summary>
        /// true once we have cached the basic sudo schema info for our collection
        /// </summary>
        private static bool _propertiesCached;

        private readonly List<T> _items = new List<T>();

        /// <summary>
        /// internal id to the collection that is used to store the next id to use
        /// </summary>
        private int _nextId;

        private StreamWriter _streamWriter;

        private string cleanCollectionHead = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
                                             + "<Collection\r\n"
                                             + "\txmlns=\"http://schemas.microsoft.com/collection/metadata/2009\"\r\n"
                                             +
                                             "\txmlns:p=\"http://schemas.microsoft.com/livelabs/pivot/collection/2009\"\r\n"
                                             + "\tName=\"{0}\"\r\n"
                                             + "\tSchemaVersion=\"1.0\">\r\n"
                                             + "\t\t<FacetCategories>";

        private string smallCollectionHead = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                                             + "<Collection"
                                             + " xmlns=\"http://schemas.microsoft.com/collection/metadata/2009\""
                                             +
                                             " xmlns:p=\"http://schemas.microsoft.com/livelabs/pivot/collection/2009\""
                                             + " Name=\"{0}\""
                                             + " SchemaVersion=\"1.0\">"
                                             + "<FacetCategories>";

        /// <summary>
        /// Simple Constructor
        /// </summary>
        public PivotCollection()
        {
            if (!_propertiesCached)
                cacheProperties();
            DeepZoomImageDir = "DeepZoomImages";
            DeepZoomCollectionImageDir = "DeepZoomCollectionImages";
            ImageDownloadDir = "DownloadedImages";
            setDefaults(); // sets default values for imageCreator as well as for collectionCreator
            ActionChanged += (s, e) =>
            {
                if (_logFile != null)
                    _logFile.WriteLine(s.ToString());
            };
        }

        /// <summary>
        /// Folder that the images and supporting xml will be dumped to after the Deep Zoom Composer has 
        /// had its way with your images (default to DeepZoomImages in constructor call)
        /// </summary>
        private string DeepZoomImageDir { get; set; }

        /// <summary>
        /// this directory is where the Deep Zoom Collection Images will be output into 
        /// (default DeepZoomCollectionImages
        /// </summary>
        private string DeepZoomCollectionImageDir { get; set; }

        /// <summary>
        /// this is the folder where remote images will be downloaded to.
        /// </summary>
        private string ImageDownloadDir { get; set; }

        /// <summary>
        /// The name of the collection 
        /// </summary>
        public string CollectionName { get; set; }

        /// <summary>
        /// when true all images added will be processed it wont try to match any up (default is false
        /// </summary>
        public bool AllImagesUnique
        {
            get { return _allImagesUnique; }
            set
            {
                if(value != _allImagesUnique)
                {
                    if (Items.Count > 0)
                        // this currently is not needed. but it will be important once we can get some AJAX functionality so that the
                        // collection can be modified on the fly based on ajax request
                        throw new Exception("Value can't be changed after any items are added to the collection");
                    _allImagesUnique = value;
                }
            }
        }

        /// <summary>
        /// when this is set to false it will not fire off the ActionChanged Event that tells
        /// you what it is currently working on
        /// </summary>
        public bool SupressActionChanged { get; set; }

        /// <summary>
        /// List of the _items in the current collection
        /// </summary>
        public List<T> Items
        {
            get { return _items; }
        }

        /// <summary>
        /// this is for debug use. When set to true (default is true) it will output clean and easy to read xml
        /// when it is set to false it will not use tabs or new lines it will be one long string of xml
        /// </summary>
        public static bool CleanOutput
        {
            get { return _cleanOutput; }
            set { _cleanOutput = value; }
        }

        

        /// <summary>
        /// This method will create the collection file at the path that you give it.
        /// </summary>
        /// <param name="fileName">file to save the output to</param>
        public void WriteCollectionToFileSystem(string fileName)
        {
            FileInfo fi = new FileInfo(fileName);
            _outputDir = fi.DirectoryName;
            if (!Directory.Exists(_outputDir))
                Directory.CreateDirectory(_outputDir);
            DeepZoomImageDir = _outputDir + Path.DirectorySeparatorChar + "DeepZoomImages" + Path.DirectorySeparatorChar;
            if (!Directory.Exists(DeepZoomImageDir))
                Directory.CreateDirectory(DeepZoomImageDir);
            DeepZoomCollectionImageDir = _outputDir + Path.DirectorySeparatorChar + "DeepZoomCollectionImages" +
                                         Path.DirectorySeparatorChar;
            if (!Directory.Exists(DeepZoomCollectionImageDir))
                Directory.CreateDirectory(DeepZoomCollectionImageDir);
            ImageDownloadDir = _outputDir + Path.DirectorySeparatorChar + "DownloadedImages" +
                               Path.DirectorySeparatorChar;
            if (!Directory.Exists(ImageDownloadDir))
                Directory.CreateDirectory(ImageDownloadDir);
            _logFile = new StreamWriter(_outputDir + Path.DirectorySeparatorChar + "PivotCollection.log");
            _logFile.AutoFlush = true;
            
            _streamWriter = new StreamWriter(fileName);
            _streamWriter.AutoFlush = true;
            writeCollectionHead();
            var collectionItems = new List<Item>(Items.Count);
            sendAction("\tWritting cxml file");
            _streamWriter.Write(CleanOutput
                                    ? "\t<Items ImgBase=\"images.dzc\" >\r\n"
                                    : "<Items ImgBase=\"images.dzc\" >");
            foreach (T item in Items)
            {
                
                var collectionItem = new Item(_nextId++, item);
                if (collectionItem.Image != null)
                {
                    sendAction(string.Format("\t\tProcessing Facets for item named {0}", collectionItem.Name));
                    collectionItem.CreateFacets(_collectionFacets);
                    if (processImage(collectionItem))
                        _streamWriter.Write(collectionItem.CollectItem());
                    sendAction(string.Format("\t\tFinished Processing item named {0}", collectionItem.Name));
                }
                else
                    sendAction(string.Format("\t\tSkipping item named {0} because its Image is null", collectionItem.Name));
            }
            _streamWriter.Write(CleanOutput
                                    ? "\t</Items>\r\n</Collection>"
                                    : "</Items></Collection>");
            sendAction("/tFinished with cxml file!");
            sendAction(string.Format("\r\n\r\nFinished processing your Images writting image collection to {0}",
                                     _outputDir + Path.DirectorySeparatorChar + "images.dzc"));
            var data = collectionCreator.Create(images, _outputDir + Path.DirectorySeparatorChar + "images.dzc");
            sendAction(
                string.Format(
                    "\r\n\r\nFinished processing your Images and all the data in the collection writting collection to {0}",
                    fileName));
            _streamWriter.Close();
            _logFile.Close();
        }

        
        /// <summary>
        /// this method will write the facet categories as well as the header for the cxml
        /// </summary>
        private void writeCollectionHead()
        {
            // check if we are using the clean output or condenced output header
            string head = string.Format(CleanOutput ? cleanCollectionHead : smallCollectionHead, CollectionName);
            _streamWriter.Write(head);
            // check if we are using the clean output or condenced output facet category template
            string template = CleanOutput
                                  ? "\t\t\t<FacetCategory Name=\"{0}\" Type=\"{1}\" p:IsFilterVisible=\"true\" p:IsWordWheelVisible=\"true\" p:IsMetaDataVisible=\"true\" />\r\n"
                                  : "<FacetCategory Name=\"{0}\" Type=\"{1}\" p:IsFilterVisible=\"true\" p:IsWordWheelVisible=\"true\" p:IsMetaDataVisible=\"true\" />";

            foreach (Facet facet in _collectionFacets)
            {
                _streamWriter.Write(string.Format(template, facet.FacetDisplayName, facet.FacetType));
            }
            _streamWriter.Write(CleanOutput ? "\t\t</FacetCategories>\r\n" : "</FacetCategories>");
        }

        /// <summary>
        /// this method is called only once in the life cycle of the application for each (T)ype of collection that is created.
        /// </summary>
        private void cacheProperties()
        {
            // find all properties that have a PivotItem Attribute
            IEnumerable<PropertyInfo> properties =
                typeof (T).GetProperties().Where(
                    p => p.GetCustomAttributes(false).Any(a => a.GetType() == typeof (PivotItemAttribute)));
            var facets = new List<Facet>();
            foreach (PropertyInfo property in properties)
            {
                // grab the PivotItem Attribute for this property if there is one
                var pivotItemInfo =
                    property.GetCustomAttributes(typeof (PivotItemAttribute), false).FirstOrDefault() as
                    PivotItemAttribute;
                if (pivotItemInfo != null)
                {
                    if (pivotItemInfo.IsFacet)
                    {
                        var facet = new Facet(property.Name, pivotItemInfo.FacetDisplayName, pivotItemInfo.FacetType);
                        if (pivotItemInfo.IsCollection) // a collection needs to be able to support many _items or it does quite a poor job at being a collection
                            facet.FacetOccurrenceLimit = FacetOccurrenceLimits.Many;
                        else // if it is not marked as a collection then it is by default only allowed one value
                            facet.FacetOccurrenceLimit = FacetOccurrenceLimits.Single;
                        facets.Add(facet);
                    }
                    else
                    {
                        if (pivotItemInfo.IsName) // is this the name property
                            _nameProperty = property;
                        if (pivotItemInfo.IsHerf) // is this the link property
                            _herfProperty = property;
                        if (pivotItemInfo.IsImage) // is this the image property
                            _imageProperty = property;
                        if (pivotItemInfo.IsDescription)
                            _descProperty = property;
                    }
                }
            }
            if (_nameProperty == null) // by the end of this call it needes to have found the property that is marked as IsName
                throw new Exception(
                    "Name is not optional. Please make one of the properties of this class as IsName=true on its PivotItem Attribute");
            _collectionFacets = facets.ToArray();
            _propertiesCached = true;
        }

        /// <summary>
        /// Method that is used to get the link for the supplied instance of T
        /// </summary>
        /// <param name="item">instance to use </param>
        /// <returns>xml friendly attribute or string null</returns>
        private static string GetHerf(T item)
        {
            if (_herfProperty == null)
                return "";
            object obj = _herfProperty.GetValue(item, null);
            if (obj != null)
                return " Href=\"" + obj + "\"";
            return "";
        }

        /// <summary>
        /// Method that is used to get the name for the supplied instance of T
        /// </summary>
        /// <param name="item">instance to use </param>
        /// <returns>xml friendly attribute or string null</returns>
        private static string GetName(T item)
        {
            string name = "";
            object obj = _nameProperty.GetValue(item, null);
            if (obj != null)
                name = obj.ToString();
            return " Name=\"" + name + "\"";
        }

        /// <summary>
        /// Method that is used to get the image for the supplied instance of T
        /// </summary>
        /// <param name="item">instance to use </param>
        /// <returns>xml friendly attribute or string null</returns>
        private static string GetImage(T item)
        {
            if (_imageProperty == null)
                return "";
            object obj = _imageProperty.GetValue(item, null);
            if (obj != null)
                return " Img=\"" + obj + "\"";
            return "";
        }

        private static string GetDescription(T item)
        {
            if (_descProperty == null)
                return "";
            object obj = _descProperty.GetValue(item, null);
            if (obj != null)
                return obj.ToString();
            return "";
        }

        #region Nested type: Facet

        /// <summary>
        /// A Facet is some property that the item will have a given value or a collection of values for.
        /// </summary>
        internal class Facet
        {
            private const string DATETIME_TEMPLATE = "<DateTime Value=\"{0}\" />";
            private const string FACET_TEMPLATE = "<Facet Name=\"{0}\">{1}</Facet>";
            private const string LINK_TEMPLATE = "<Link Name=\"{0}\" Href=\"{1}\" />";
            private const string NUMBER_TEMPLATE = "<Number Value=\"{0}\" />";
            private const string STRING_TEMPLATE = "<String Value=\"{0}\" />";

            /// <summary>
            /// list of current values for this _items facet
            /// </summary>
            private readonly List<object> values = new List<object>();

            private FacetOccurrenceLimits _facetOccurrenceLimit;

            /// <summary>
            /// internal constructor used for basic creation with no display name
            /// </summary>
            /// <param name="name"></param>
            internal Facet(string name)
            {
                FacetName = name;
                FacetDisplayName = name;
            }

            /// <summary>
            /// internal constructor used for creation with a display name
            /// </summary>
            /// <param name="name">programmer facet name (defaults to the property name 
            /// the associated property in T</param>
            /// <param name="displayName">display name for this property defined in the ItemAttributes.DisplayName</param>
            internal Facet(string name, string displayName)
            {
                FacetName = name;
                FacetDisplayName = displayName;
            }

            /// <summary>
            /// internal constructor used for creation with a display name
            /// </summary>
            /// <param name="name">programmer facet name (defaults to the property name 
            /// the associated property in T</param>
            /// <param name="displayName">display name for this property defined in the ItemAttributes.DisplayName</param>
            /// <param name="type">type of facet </param>
            internal Facet(string name, string displayName, FacetTypes type)
            {
                FacetName = name;
                FacetDisplayName = displayName;
                FacetType = type;
            }

            internal string FacetName { get; private set; }
            internal string FacetDisplayName { get; private set; }

            /// <summary>
            /// type of data that this facet holds 
            /// </summary>
            internal FacetTypes FacetType { get; set; }

            /// <summary>
            /// this sets if a facet can have more than 1 value (like an array)
            /// </summary>
            internal FacetOccurrenceLimits FacetOccurrenceLimit
            {
                get { return _facetOccurrenceLimit; }
                set
                {
                    if (_facetOccurrenceLimit != value)
                    {
                        _facetOccurrenceLimit = value;
                        // if we are changing to only allow 1 value clear out all but the last set value
                        if (_facetOccurrenceLimit == FacetOccurrenceLimits.Single && values.Count > 1)
                            values.RemoveRange(0, values.Count - 2);
                    }
                }
            }

            /// <summary>
            /// method is used to add a value to a facet and return what index that value is 
            /// in the collection of values for that facet 
            /// </summary>
            /// <param name="value">value to set</param>
            /// <returns>index of value in the collection (in single mode it will always be 0)</returns>
            internal int AddValue(object value)
            {
                if (value == null)
                    return -1;
                //check to see if we are allowed to have more than one value and based on that and if we already have a value either add or replace the value
                if (_facetOccurrenceLimit == FacetOccurrenceLimits.Single && values.Count == 1)
                {
                    values[0] = value;
                    return 0;
                }
                values.Add(value);
                return values.Count - 1;
            }

            /// <summary>
            /// this method will pull out all the values from the backing instance of T for this item for the property
            /// that backs this facet
            /// </summary>
            /// <param name="backingItem">the instance of T to read the facet's backing property (Property MUST be IEnumerable)</param>
            internal void ReadCollection(T backingItem)
            {
                values.Clear();
                var items = typeof (T).GetProperty(FacetName).GetValue(backingItem, null) as IEnumerable;
                if (items == null)
                    return;
                foreach (object o in items)
                {
                    values.Add(o);
                }
            }

            /// <summary>
            /// this method will collect all the information that is needed for this facet
            /// </summary>
            /// <returns></returns>
            internal string CollectFacet()
            {
                if (values.Count == 0)
                    return "";
                string template = "";
                switch (FacetType)
                {
                    case FacetTypes.DateTime:
                        template = DATETIME_TEMPLATE;
                        break;
                    case FacetTypes.Item:
                        throw new NotImplementedException("Item Facet types are not yet supported");
                    case FacetTypes.Link:
                        throw new NotImplementedException("Link Facet types are not yet supported");
                    case FacetTypes.Number:
                        template = NUMBER_TEMPLATE;
                        break;
                    default:
                        template = STRING_TEMPLATE;
                        break;
                }

                string fullFacet = CleanOutput ? "\r\n" : "";
                foreach (object value in values)
                {
                    // check to see if clean output is on and if so indent this with three tabs
                    fullFacet += (CleanOutput
                                      ? "\t\t\t"
                                      : "") + string.Format(template, value);
                    //if clean output is on add a new line
                    fullFacet += CleanOutput ? "\r\n" : "";
                }
                // if clean output is on then add 2 tabs to the end of this facet so that it lines up all pretty
                fullFacet += CleanOutput ? "\t\t" : "";
                // put the facet into the facet template
                return (CleanOutput
                            ? "\t\t"
                            : "") + string.Format(FACET_TEMPLATE, FacetDisplayName, fullFacet) +
                       (CleanOutput ? "\r\n" : "");
            }
        }

        #endregion

        #region Nested type: Item

        /// <summary>
        /// used to store each item in the collection and each _items facets
        /// </summary>
        internal class Item
        {
            private readonly T associatedItem;

            /// <summary>
            /// list of the facets for this item
            /// </summary>
            private readonly Dictionary<string, Facet> facets = new Dictionary<string, Facet>();

            /// <summary>
            /// internal constructor
            /// </summary>
            /// <param name="id">the id for this item in the collection</param>
            /// <param name="item">the item that will be added</param>
            internal Item(int id, T item)
            {
                Id = id;
                associatedItem = item;
            }

            /// <summary>
            /// the internal id used for linking up images to _items
            /// </summary>
            internal int Id { get; private set; }

            private int? _imageId;
            internal int ImageId { get
            {
                return _imageId.HasValue ? _imageId.Value : Id;
            } set
            {
                _imageId = value;  
            }}

            /// <summary>
            /// this is the image file that will be used with this item in the collection
            /// </summary>
            internal string Image
            {
                get { return GetImage(associatedItem); }
            }
            internal string ImagePath
            {
                get
                {
                    return _imageProperty.GetValue(associatedItem, null) as string;
                }
            }

            /// <summary>
            /// this is the link that will be used with this item in the collection
            /// </summary>
            internal string Href
            {
                get { return GetHerf(associatedItem); }
            }

            /// <summary>
            /// this is the name that will be used with this item in the collection
            /// </summary>
            internal string Name
            {
                get { return GetName(associatedItem); }
            }

            /// <summary>
            /// this is the description that will be used with this item in the collection
            /// </summary>
            internal string Description
            {
                get { return GetDescription(associatedItem); }
            }

            /// <summary>
            /// build out the facets for this item based on the collection facets.
            /// </summary>
            /// <param name="collectionFacets">facets passed in from the collection</param>
            internal void CreateFacets(Facet[] collectionFacets)
            {
                foreach (Facet facet in collectionFacets)
                {
                    var f = new Facet(facet.FacetName, facet.FacetDisplayName, facet.FacetType);
                    f.FacetOccurrenceLimit = facet.FacetOccurrenceLimit;
                    facets.Add(facet.FacetName, f);
                }
            }


            /// <summary>
            /// this method reads the values of the facets backing properties
            /// </summary>
            private void grabFacets()
            {
                foreach (var pair in facets)
                {
                    if (pair.Value.FacetOccurrenceLimit == FacetOccurrenceLimits.Single)
                    {
                        object value = typeof (T).GetProperty(pair.Key).GetValue(associatedItem, null);
                        pair.Value.AddValue(value);
                    }
                    else
                        pair.Value.ReadCollection(associatedItem);
                }
            }

            /// <summary>
            /// this method grabs all info needed for this item to be added to the collection
            /// </summary>
            /// <returns></returns>
            internal string CollectItem()
            {
                string facetString = CleanOutput ? "\t\t\t<Facets>\r\n" : "<Facets>";
                grabFacets();
                string item =
                    string.Format(
                        CleanOutput
                            ? "\t\t<Item Id=\"{0}\" Img=\"#{0}\" {1} {2} >\r\n"
                            : "<Item Id=\"{0}\" Img=\"#{0}\" {1} {2} >",
                        Id, Name, Href);

                if (Description.Length > 0)
                    item +=
                        string.Format(
                            CleanOutput ? "\t\t\t<Description>{0}</Description>\r\n" : "<Description>{0}</Description>",
                            Description);

                //string facetOutput = "";
                Facet[] items = facets.Select(p => p.Value).ToArray();

                int i = 0;
                foreach (Facet facet in items)
                {
                    string str = facet.CollectFacet();
                    if (str.Length > 0)
                        i++;
                    facetString += str;
                }
                if (i > 0)
                    item += facetString + (CleanOutput ? "\t\t\t</Facets>\r\n" : "</Facets>");
                item += CleanOutput ? "\t\t</Item>\r\n" : "</Item>";
                return item;
            }
        }

        #endregion

        #endregion


        #region Image Processing 

        private bool processImage(Item collectionItem)
        {
            try
            {
                if (!AllImagesUnique) // check to see if we have processed this exact file before.
                    if (cacheOfImageIds.ContainsKey(collectionItem.ImagePath)) // we already have have processed this image so don't do it again
                    {
                        collectionItem.ImageId = cacheOfImageIds[collectionItem.ImagePath];
                        return true;
                    }

                string workingImage = GetImageForDeepZoomComposer(collectionItem.ImagePath);
                string deepZoomImage = DeepZoomImageDir + collectionItem.ImageId;

                sendAction(string.Format("\tConverting image {0} to Deep Zoom Output file {1} ", workingImage,
                                                 deepZoomImage));
                imageCreator.Create(workingImage, deepZoomImage);
                SurrogateImageInfo sii = new SurrogateImageInfo(workingImage, deepZoomImage + ".xml");
                images.Add(sii);
                if (!AllImagesUnique) // if we want to make sure we don't use the Deep Zoom Composer on the same file twice then add this image to the cache
                    cacheOfImageIds.Add(collectionItem.ImagePath, collectionItem.Id);
                return true;
            }
            catch (Exception ex)
            {
                sendAction(
                    string.Format(
                        "\tSkipping current item cause of execption encountered while processing the image\r\n\t\t{0}",
                        ex.Message));
                return false; // this item will not be added to the collection
            }
        }

        /// <summary>
        /// this method checks to see if the image contains http:// or https:// 
        /// if it does it fires up the WebClient and downloads it and returns the local 
        /// file name for Deep Zoom Composer to work with.
        /// 
        /// If the input file path does not have http:// or https:// it just returns the input string
        /// </summary>
        /// <param name="imageFile"></param>
        /// <returns></returns>
        internal string GetImageForDeepZoomComposer(string imageFile)
        {
            if (imageFile.StartsWith("http://") || imageFile.StartsWith("https://"))
            {
                if (_client == null) // if we havent used the WebClient for this collection yet create one
                    _client = new WebClient();

                string tempFile = ImageDownloadDir + Guid.NewGuid();
                sendAction(string.Format("\tDownloading image '{0} to {1}", imageFile, tempFile));
                _client.DownloadFile(imageFile, tempFile);
                return tempFile;
            }
            return imageFile;
        }


        /// <summary>
        /// this is used to store references to items already Deep Zoom Image Converted
        /// </summary>
        private Dictionary<string, int> cacheOfImageIds = new Dictionary<string, int>(300);

        /// <summary>
        /// collection of images that have been converted to deep zoom format
        /// </summary>
        private List<SurrogateImageInfo> images = new List<SurrogateImageInfo>();

        /// <summary>
        /// Deep Zoom Collection Creator instance to use for this Pivot collection
        /// </summary>
        private CollectionCreator collectionCreator = new CollectionCreator();

        /// <summary>
        /// Instance of Deep Zoom Image Creator for use with this Pivot Collection and all images in it 
        /// </summary>
        private ImageCreator imageCreator = new ImageCreator();

        /// <summary>
        /// Instance of WebClient that will be loaded when needed if any images need to be downloaded
        /// </summary>
        private WebClient _client;

        private void setDefaults()
        {
            SupressActionChanged = true;
            collectionCreator.ImageQuality = 0.95D;
            collectionCreator.MaxLevel = 8;
            collectionCreator.TileSize = 256;
            collectionCreator.TileFormat = ImageFormat.Jpg;
            collectionCreator.ServerFormat = ServerFormats.Default;

            imageCreator.ServerFormat = ServerFormats.Default;
            imageCreator.TileFormat = ImageFormat.AutoSelect;
            imageCreator.TileSize = 256;
            imageCreator.MaxLevel = 8;
            imageCreator.ImageQuality = 0.95D;
            imageCreator.CopyMetadata = true;

            DeepZoomImageDir = _outputDir + Path.DirectorySeparatorChar + "DeepZoomImages" + Path.DirectorySeparatorChar;
            if (!Directory.Exists(DeepZoomImageDir))
                Directory.CreateDirectory(DeepZoomImageDir);
            DeepZoomCollectionImageDir = _outputDir + Path.DirectorySeparatorChar + "DeepZoomCollectionImages" +
                                         Path.DirectorySeparatorChar;
            if (!Directory.Exists(DeepZoomCollectionImageDir))
                Directory.CreateDirectory(DeepZoomCollectionImageDir);
            ImageDownloadDir = _outputDir + Path.DirectorySeparatorChar + "DownloadedImages" +
                               Path.DirectorySeparatorChar;
            if (!Directory.Exists(ImageDownloadDir))
                Directory.CreateDirectory(ImageDownloadDir);
        }

        

        #endregion

        private void sendAction(string text)
        {
            if (!SupressActionChanged)
                if (ActionChanged != null)
                    ActionChanged(text, null);
        }
    }
} 